# 日志系统主要有4个组成部分
#   1. logger 日志系统对外接口，直接使用logger进行日志输出
#   2. handler 处理器将日志数据发送到输出位置(stdout、file、network...)
#   3. filter 过滤器，过滤那些日志需要被输出，哪些不需要
#   4. formatter 格式化操作，将日志格式化为指定格式
import logging

def f1_mylogger():
    # 创建日志
    log = logging.getLogger('myLogger')
    log.setLevel(logging.DEBUG) # 设置日志级别

    console_handler = logging.StreamHandler() # 创建控制台handler
    # file_handler = logging.FileHandler(filename, mode, encoding, delay) # 文件handler
    console_handler.setLevel(logging.DEBUG) # 设置level级别

    # 创建日志格式化类formatter
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s: %(message)s')
    # 设置控制台handler的格式化对象
    console_handler.setFormatter(formatter)

    # 将handler添加到自定义log
    log.addHandler(console_handler)
    # NullHandler什么都不做，如果不想输出任何信息，加入该handler
    # log.addHandler(logging.NullHandler())

    log.debug('debug level')
    log.info('info level')
    log.warning('warn level')
    log.error('error level')
    log.critical('critical level')

def f2_file_config():
    # 配置文件形式
    import logging.config
    # 加载配置文件
    logging.config.fileConfig('logging.conf')
    # 创建logger
    log = logging.getLogger('demo')
    # 可以启动本地端口监听，通过socket发送日志文件实时更新
    # logging.config.listen(1234).start()
    # 发送日志给服务器
    # import socket, sys, struct
    # with open('logging.conf', 'rb') as f:
    #     data_to_send = f.read()
    # HOST = 'localhost'
    # PORT = 1234
    # s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # print('connecting...')
    # s.connect((HOST, PORT))
    # print('sending config...')
    # s.send(struct.pack('>L', len(data_to_send)))
    # s.send(data_to_send)
    # s.close()
    # print('complete')

    log.debug('debug message')
    log.info('info message')
    log.warning('warn message')
    log.error('error message')
    log.critical('critical message')

def f3_dict_config():
    # 字典型配置文件 支持yml json文件形式，yml实际上读取加载后转换为字典类型也是json格式的
    # yaml配置需要使用yaml解析模块(pyyaml) python -m pip install pyyaml
    import yaml
    import logging.config
    # 读取yml文件
    dict_config = yaml.safe_load(open('logging.yml'))
    print(dict_config)
    # dictConfig参数是一个字典对象
    logging.config.dictConfig(dict_config)
    log = logging.getLogger('demo1')
    log.debug('debug message')
    log.info('info message')
    log.warning('warn message')
    log.error('error message')
    log.critical('critical message')

def f4_opt():
    log = logging.getLogger("myLogger")
    class DemoObj:
        def __str__(self):
            return 'demo obj __str__ method'
    # logger可以打印任意对象，只要对象实现了__str__方法
    log.warning(DemoObj())

    # 日志性能优化
    # 对于日志打印，格式化操作相对来说会消耗一些性能
    # 比如：打印日志级别为info级别，但是又有debug日志为了避免format操作，可以使用isEnableFor
    if log.isEnabledFor(logging.INFO): # 如果没启用info日志则不会执行后面的操作，减少性能损耗
        log.info('%s is %s', 'a', 'b')
    # 注意：对于isEnabledFor判断有些情况性能损耗可能大于日志格式化，比如日志父子关系较深
    # a.b.c.d.e 这类日志，只有在最高层日志定义了level，则最底层日志需要一直往上找level耗费性能，
    # 该种情况可以将结果存储临时变量，不用每次都计算level级别
    # 其他优化项
    #   1. logging._srcfile=None, pypy环境下能提升代码性能
    #   2. logging.logThreads=0 禁用线程信息 
    #   3. logging.logProcesses=0 禁用进程信息
    # 此外python logging模块包含了基本的handlers实现，只要不引入logging.handlers和logging.config模块不会增加内存占用

# 日志使用说明(类似java的logging日志系统，如logback)
# 1. 日志对象logger可以跨模块使用，在一个模块创建日志对象后，其他模块可以通过getLogger(name)获取并使用
#    在当前模块中配置的日志，被import进来的模块内logging也会遵循当前模块配置
# 2. 多线程下，日志对象也可以共用
# 3. 同一个日志对象可以配置多个handler，输出到不同的位置(console、file、mail、socket等)
# 更多高级用法参照文档 -> Python HOWTOS -> Logging cookbook

if __name__ == '__main__':
    # f1_mylogger()
    # f2_file_config()
    # f3_dict_config()
    f4_opt()